﻿using System;
using System.Collections.Generic;
using System.Runtime.Remoting;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Text;

namespace Defecter.Contract
{
	#region Class WCFException
	/// <summary>
	/// Special case exception that will be reconstituted on the remote side
	/// of a WCF channel
	/// </summary>
	/// <remarks>
	/// The InnerException property will be the real exception that was thrown.
	/// The Message property will be the same message as the original exception.
	/// </remarks>
	public class WCFException: Exception
	{
		#region WCFException - Types
		#endregion

		#region WCFException - Fields
		static public readonly string								TypeName = typeof(WCFException).ToString();
		internal string															wcfe_stacktrace;
		internal string															wcfe_faulting_method;
		#endregion

		#region WCFException - Delegates and Events
		#endregion

		#region WCFException - Properties
		/// <summary>
		/// Gets the reconstructed name of the method that threw the exception
		/// </summary>
		public string FaultingMethod
		{
			get
			{
				return wcfe_faulting_method;
			}
		}		/* property WCFException FaultingMethod */

		/// <summary>
		/// Gets the stack trace of the original exception
		/// </summary>
		public override string StackTrace
		{
			get
			{
				return wcfe_stacktrace;
			}
		}		/* property WCFException StackTrace */
		#endregion

		#region WCFException - Constructors
		internal WCFException (Exception thrownException)
			: this(thrownException.Message, thrownException)
		{
		}		/* constructor WCFException */

		internal WCFException (string message, Exception thrownException)
			: base(message, thrownException)
		{
		}		/* constructor WCFException */
		#endregion

		#region WCFException - Methods
		#endregion
	}			/* class WCFException */
	#endregion

	#region Class WCFFault
	/// <summary>
	/// General fault class to throw across WCF channels
	/// </summary>
	/// <remarks>
	/// This class should be specified as the FaultContract on any operation
	/// that might throw the exception.
	/// </remarks>
	[Serializable]
	[DataContract]
	public class WCFFault
	{
		#region WCFFault - Types
		#endregion

		#region WCFFault - Fields
		[DataMember]
		private ExceptionDetail											wcff_exception_detail;
		[DataMember]
		private string															wcff_faulting_method;
		#endregion

		#region WCFFault - Delegates and Events
		#endregion

		#region WCFFault - Properties
		public string DisplayString
		{
			get
			{
				return InnerException.Message.Trim(new char[] { '\r', '\n' });
			}
			set
			{
			}
		}		/* property WCFFault IViewableItemModel.DisplayString */

		public bool IsWCFException
		{
			get
			{
				return wcff_exception_detail.Type == WCFException.TypeName;
			}
		}		/* property WCFFault IsWCFException */

		public string FaultingMethod
		{
			get
			{
				return wcff_faulting_method;
			}
		}		/* property WCFFault FaultingMethod */

		/// <summary>
		/// The exception detail
		/// </summary>
		/// <remarks>
		/// </remarks>
		public ExceptionDetail InnerException
		{
			get
			{
				return wcff_exception_detail;
			}
		}		/* property WCFFault InnerException */

		/// <summary>
		/// </summary>
		/// <remarks>
		/// </remarks>
		public string StackTrace
		{
			get
			{
				return InnerException.StackTrace;
			}
		}		/* property WCFFault InnerException */
		#endregion

		#region WCFFault - Constructors
		public WCFFault (Exception ex)
		{
			wcff_faulting_method = ex.TargetSite.DeclaringType.Module.Assembly.FullName + " - " + ex.TargetSite.DeclaringType.FullName;
			wcff_exception_detail = new ExceptionDetail(ex);
		}		/* constructor WCFFault */
		#endregion

		#region WCFFault - Methods
		/// <summary>
		/// Creates the whole package ready to throw
		/// </summary>
		/// <param name="ex"></param>
		/// <param name="reason"></param>
		/// <returns></returns>
		public static FaultException<WCFFault> CreateFaultException (Exception ex, string reason)
		{
			WCFFault															fault;

			fault = new WCFFault(ex);
			return new FaultException<WCFFault>(fault, reason);
		}		/* method WCFFault CreateFaultException */

		/// <summary>
		/// Reconstructs the exception on the remote channel end
		/// </summary>
		/// <returns></returns>
		private Exception Reconstitute (ExceptionDetail exd)
		{
			Type																			exception_type;

			if (exd.Type == typeof(WCFException).ToString())
				exception_type = typeof(WCFException);
			else
				if (exd.Type == typeof(ApplicationException).ToString())
					exception_type = typeof(ApplicationException);
				else
					exception_type = typeof(Exception);
			if (exd.InnerException == null)
				return (Exception)Activator.CreateInstance(exception_type, new object[] { exd.Message });
			else
				return (Exception)Activator.CreateInstance(exception_type, new object[] { exd.Message, Reconstitute(exd.InnerException) });
		}		/* method WCFFault Reconstitute */

		/// <summary>
		/// Reconstructs the exception on the remote channel end
		/// </summary>
		/// <returns></returns>
		public WCFException Reconstitute ()
		{
			Exception																	thrown_ex;
			WCFException															result;

			thrown_ex = Reconstitute(wcff_exception_detail);
			if (thrown_ex is WCFException)
				result = (WCFException)thrown_ex;
			else
				result = new WCFException(thrown_ex);
			result.wcfe_stacktrace = StackTrace;
			result.wcfe_faulting_method = wcff_faulting_method;
			return result;
		}		/* method WCFFault Reconstitute */
		#endregion
	}			/* class WCFFault */
	#endregion
	
	#region Class WCFFaultHandlerAttribute
	/// <summary>
	/// Custom attribute to cause the implementation of IErrorHadler to apply
	/// to the service.
	/// </summary>
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
	public class WCFFaultHandlerAttribute: Attribute, System.ServiceModel.Description.IContractBehavior
	{
		#region WCFFaultHandlerAttribute - Types
		#endregion

		#region WCFFaultHandlerAttribute - Fields
		private Type																error_handler_type;
		#endregion

		#region WCFFaultHandlerAttribute - Delegates and Events
		#endregion

		#region WCFFaultHandlerAttribute - Properties
		#endregion

		#region WCFFaultHandlerAttribute - Constructors
		public WCFFaultHandlerAttribute (Type errorHandlerType)
		{
			error_handler_type = errorHandlerType;
		}		/* constructor WCFFaultHandlerAttribute */
		#endregion

		#region WCFFaultHandlerAttribute - Methods
		public void AddBindingParameters (System.ServiceModel.Description.ContractDescription contractDescription,
			System.ServiceModel.Description.ServiceEndpoint serviceEndpoint,
			System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
		{
		}		/* method WCFFaultHandlerAttribute AddBindingParameters */

		public void ApplyClientBehavior (System.ServiceModel.Description.ContractDescription contractDescription,
			System.ServiceModel.Description.ServiceEndpoint serviceEndpoint,
			System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
		{
		}		/* method WCFFaultHandlerAttribute ApplyClientBehavior */

		public void ApplyDispatchBehavior (System.ServiceModel.Description.ContractDescription contractDescription,
			System.ServiceModel.Description.ServiceEndpoint serviceEndpoint,
			System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
		{
			dispatchRuntime.ChannelDispatcher.ErrorHandlers.Add((System.ServiceModel.Dispatcher.IErrorHandler)Activator.CreateInstance(error_handler_type));
		}		/* method WCFFaultHandlerAttribute ApplyDispatchBehavior */

		public void Validate (System.ServiceModel.Description.ContractDescription contractDescription,
			System.ServiceModel.Description.ServiceEndpoint serviceEndpoint)
		{
		}		/* method WCFFaultHandlerAttribute Validate */
		#endregion
	}			/* class WCFFaultHandlerAttribute */
	#endregion

	#region Class WCFFaultHandler
	/// <summary>
	/// Handles all exceptions thrown across the WCF connection
	/// </summary>
	/// <remarks>
	/// Implements IErrorHandler to capture exception before it crosses the WCF connection.
	/// </remarks>
	public class WCFFaultHandler: IErrorHandler
	{
		#region WCFFaultHandler - Types
		#endregion

		#region WCFFaultHandler - Fields
		#endregion

		#region WCFFaultHandler - Delegates and Events
		#endregion

		#region WCFFaultHandler - Properties
		#endregion

		#region WCFFaultHandler - Constructors
		public WCFFaultHandler ()
			: base()
		{
		}		/* constructor WCFFaultHandler */
		#endregion

		#region WCFFaultHandler - Methods
		/// <summary>
		/// Says we handled the error
		/// </summary>
		/// <param name="ex"></param>
		/// <returns></returns>
		public bool HandleError (Exception ex)
		{
			return true;
		}		/* method WCFFaultHandler HandlerError */

		/// <summary>
		/// Provides the fault to return over the channel
		/// </summary>
		/// <param name="ex"></param>
		/// <param name="messageVersion"></param>
		/// <param name="faultMessage"></param>
		/// <remarks>
		/// The fault must be a type indicated on the faulting method at compile time or
		/// the WCF channel will be faulted.
		/// <para>
		/// This implementation always creates a WCFFault object to throw
		/// </para>
		/// </remarks>
		public void ProvideFault (Exception ex, MessageVersion messageVersion, ref Message faultMessage)
		{
			FaultException<WCFFault>							fe;

			if (ex is FaultException<WCFFault>)
				fe = (FaultException<WCFFault>)ex;
			else
				fe = new FaultException<WCFFault>(new WCFFault(ex), "WCFFaultHandler captured: " + ex.Message);
			faultMessage = Message.CreateMessage(messageVersion, fe.CreateMessageFault(), fe.Action);
		}		/* method WCFFaultHandler ProvideFault */
		#endregion
	}			/* class WCFFaultHandler */
	#endregion
}